/*
 License
 
 Copyright © 2012 Kevin Kimmich
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights to 
 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 the Software, and to permit persons to whom the Software is furnished to do so, 
 subject to the following conditions:
 
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
 FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/**
 @file
 This is a doubly linked list implementation inspired by the Linux kernel list. This is thread safe.
 */
#ifndef _list_h		
#define _list_h

#ifdef __cplusplus
extern "C" {
#endif


#include "crcore.h"

/**
 Use this for initializing a list.
 
 list_t list = LIST_INITIALIZER;
 */
#define LIST_INITIALIZER {.count=0,.head.prev=NULL,.head.next=NULL,.mutex=PTHREAD_MUTEX_INITIALIZER}
#define NODE_INITIALIZER {.next=NULL,.prev=NULL,.list=NULL};

/**
 Another way to initialize the list.
 */
static __inline__ void list_init(list_t* l)
{
	l->count = 0;
	l->head.prev = NULL;
	l->head.next = NULL;
	pthread_mutex_init(&l->mutex,NULL);
}

/**
 return 1 if the list is empty, 0 if not. This is not really
 thread safe. (potential race condition).
 */
static __inline__ int list_empty(list_t* l)
{
	int rv=1;
	pthread_mutex_lock(&l->mutex);
	if(l->count)
		rv=0;
	pthread_mutex_unlock(&l->mutex);
	return rv;
}

/**
 return the list count. This is not really thread safe
 (potential race condition).
 */
static __inline__ int list_count(list_t* l)
{
	int rv;
	pthread_mutex_lock(&l->mutex);
	rv=l->count;
	pthread_mutex_unlock(&l->mutex);
	return rv;
}

/**
 Add an item at the head of the list.
 */
static __inline__ void list_add_head(list_t* l, list_node_t* new_head)
{
	assert(new_head->list==NULL);
	new_head->list=l;
	pthread_mutex_lock(&l->mutex);
	l->count++;
	if(l->head.next)
	{
		l->head.next->prev = new_head;
		new_head->prev = NULL;
		new_head->next = l->head.next;
		l->head.next = new_head;
	}
	else
	{
		new_head->prev=NULL;
		new_head->next=NULL;
		l->head.next = new_head;
		l->head.prev = new_head;
	}
	pthread_mutex_unlock(&l->mutex);
}

/**
 Add an item at the tail of the list.
 */
static __inline__ void list_add_tail(list_t* l, list_node_t* new_tail)
{
	assert(new_tail->list==NULL);
	new_tail->list=l;
	pthread_mutex_lock(&l->mutex);
	l->count++;
	if(l->head.prev)
	{
		l->head.prev->next = new_tail;
		new_tail->next = NULL;
		new_tail->prev = l->head.prev;
		l->head.prev = new_tail;
	}
	else
	{
		new_tail->prev=NULL;
		new_tail->next=NULL;
		l->head.next = new_tail;
		l->head.prev = new_tail;
	}
	pthread_mutex_unlock(&l->mutex);
}

/**
  Add an item at the tail of the list. NOT thread safe.
 */
static __inline__ void __list_add_tail(list_t* l, list_node_t* new_tail)
{
	assert(new_tail->list==NULL);
	new_tail->list=l;

	l->count++;
	if(l->head.prev)
	{
		l->head.prev->next = new_tail;
		new_tail->next = NULL;
		new_tail->prev = l->head.prev;
		l->head.prev = new_tail;
	}
	else
	{
		new_tail->prev=NULL;
		new_tail->next=NULL;
		l->head.next = new_tail;
		l->head.prev = new_tail;
	}
}

/**
 Insert a node. NOT thread safe.
 */
static __inline__ void __insert_node(list_node_t* prev, list_node_t* next, list_node_t* node)
{
	next->prev = node;
	node->next = next;
	node->prev = prev;
	prev->next = node;
}

/**
 Insert the new node before next. Thread safe.
 */
static __inline__ void list_add_before(list_t* l, list_node_t* next, list_node_t* new_node)
{
	assert(new_node->list==NULL);
	new_node->list=l;
	pthread_mutex_lock(&l->mutex);
	l->count++;
	if(l->head.next==next)
	{
		l->head.next->prev = new_node;
		new_node->prev = NULL;
		new_node->next = l->head.next;
		l->head.next = new_node;
	}
	else
		__insert_node(next->prev, next, new_node);
	pthread_mutex_unlock(&l->mutex);
}

/**
 Insert a new node after next. Thread safe.
 */
static __inline__ void list_add_after(list_t* l, list_node_t* next, list_node_t* new_node)
{
	assert(new_node->list==NULL);
	new_node->list=l;
	pthread_mutex_lock(&l->mutex);
	l->count++;
	if(l->head.prev==next)
	{
		l->head.prev->next = new_node;
		new_node->next = NULL;
		new_node->prev = l->head.prev;
		l->head.prev = new_node;
	}
	else
		__insert_node(next, new_node, next->next);
	pthread_mutex_unlock(&l->mutex);
}

/**
 Remove a node from the list. Thread safe.
 */
static __inline__ void list_remove(list_t* l, list_node_t* node)
{
	pthread_mutex_lock(&l->mutex);
	l->count--;
	if(l->head.next == node)
		l->head.next = node->next;
	if(l->head.prev == node)
		l->head.prev = node->prev;
	
	if(node->prev)
		node->prev->next = node->next;
	if(node->next)
		node->next->prev = node->prev;
	
	node->prev = NULL;
	node->next = NULL;
	node->list = NULL;
	pthread_mutex_unlock(&l->mutex);
}

/**
 Remove a node from the list. NOT thread safe.
 */
static __inline__ void __list_remove(list_t* l, list_node_t* node)
{
	l->count--;
	if(l->head.next == node)
		l->head.next = node->next;
	if(l->head.prev == node)
		l->head.prev = node->prev;
	
	if(node->prev)
		node->prev->next = node->next;
	if(node->next)
		node->next->prev = node->prev;
	
	node->prev = NULL;
	node->next = NULL;
	node->list = NULL;
}

/**
 Cast a node to a type.
 @param type_name The name of the type to cast to.
 @param node_member_name The member of the list_node_t node in the struct.
 @param collection_node_ptr The structure that's being cast to a type.
 */
#define CAST_NODE(  type_name , node_member_name, collection_node_ptr ) ((type_name*)((void*)collection_node_ptr - offsetof(type_name,node_member_name)))

#ifdef __cplusplus
}
#endif

	
#endif //_list_h
